<?php

namespace lc\helpers;

use app\Auth;
use lc\cache\redis\Redis;
use think\facade\Config;
use think\facade\Db;

class Helper
{
    /**
     * @desc    过滤emoji表情
     * @param   $str
     * @return  null|string|string[]
     */
    public static function filterEmoji($str)
    {
        return preg_replace_callback('/./u', function (array $match) {
            return strlen($match[0]) >= 4 ? '' : $match[0];
        }, $str);
    }

    /**
     * @desc    字节转换
     * @param   int $numSize
     * @return  string
     */
    public static function sizeConvert(int $numSize)
    {
        if ($numSize >= 1073741824) {
            $charSize = round($numSize / pow(1024, 3) * 100) / 100 . ' GB';
        } elseif ($numSize >= 1048576) {
            //转成MB
            $charSize = round($numSize / pow(1024, 2) * 100) / 100 . ' MB';
        } elseif ($numSize >= 1024) {
            //转成KB
            $charSize = round($numSize / 1024 * 100) / 100 . ' KB';
        } else {
            //不转换直接输出
            $charSize = $numSize . ' B';
        }
        return $charSize;
    }

    /**
     * @desc    数字单位转换
     * @param   int $number
     * @return  string
     */
    public static function numberConvert(int $number)
    {
        if ($number >= 1000 && $number < 10000) {
            $number = sprintf('%.1f', $number / 1000) . ' K';
        } elseif ($number >= 10000) {
            $number = sprintf('%.1f', $number / 10000) . ' W';
        } elseif ($number >= 1000000) {
            $number = sprintf('%.1f', $number / 10000) . ' M';
        }
        return $number;
    }

    /**
     * @desc    生成 hash token
     * @param   string $value
     * @return  mixed
     */
    public static function hashToken(string $value)
    {
        $hash  = hash_hmac('sha1', $value . mt_rand() . time(), mt_rand(), true);
        $token = str_replace('=', '', strtr(base64_encode($hash), '+/', '-_'));

        return $token;
    }

    /**
     * @desc    是否微信环境
     * @return  bool|mixed
     */
    public static function isWeixin()
    {
        return strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') !== false ? true : false;
    }

    /**
     * @desc    ssl 加密
     * @param   $str    待加密字符
     * @param   string  $key    加密key
     * @param   string  $iv     偏移iv
     * @return  string
     */
    public static function encrypt($str, $key = 'cdlchdwxcdh5', $iv = 'cdlchd0123456789')
    {
        return bin2hex(openssl_encrypt($str, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv));
    }

    /**
     * @desc    ssl 解密
     * @param   $str    待解密字符
     * @param   string  $key    加密key
     * @param   string  $iv     偏移iv
     * @return  string
     */
    public static function decrypt($str, $key = 'cdlchdwxcdh5', $iv = 'cdlchd0123456789')
    {
        return openssl_decrypt(hex2bin($str), 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
    }

    /**
     * @desc    记录日志
     * @param   array $data
     * @param   string $remarks
     */
    public static function apiLogs(array $data, string $remarks = '')
    {
        $request = \request();
        $data = [
            'token'      => $request->header('token', null),
            'uid'        => Auth::$userId,
            'url'        => $request->url(),
            'controller' => $request->controller(),
            'func'       => $request->action(),
            'method'     => $request->method(),
            'ip'         => $request->ip(),
            'params'     => json_encode($data, 256),
            'remarks'    => $remarks,
            'day'        => strtotime('today'),
            'create'     => time()
        ];
        $rdsConf = Config::get('lc.redis');
        if ($rdsConf['log_open']) {
            $redis = Redis::instance();
            $redis->select($rdsConf['db']);
            $redis->rPush(env('id') . '_apilogs', json_encode($data, 256));
        } else {
            Db::name('api_logs')->insert($data);
        }
    }

    /**
     * @desc 	将xml转为array
     * @param 	$xml
     * @return 	array
     * @throws 	\Exception
     */
    public static function xmlToArray($xml)
    {
        if (!$xml) {
            throw new \Exception('xml数据异常！');
        }
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $data;
    }

    /**
     * @desc    输出xml字符
     * @param   array $data
     * @return  string
     * @throws 	\Exception
     */
    public static function arrayToXml($data)
    {
        if (!is_array($data) || count($data) <= 0) {
            throw new \Exception('数组数据异常！');
        }

        $xml = '<xml>';
        foreach ($data as $key => $val) {
            if (is_numeric($val)) {
                $xml .= '<' . $key . '>' . $val . '</' . $key . '>';
            } else {
                $xml .= '<' . $key . '><![CDATA[' . $val . ']]></' . $key . '>';
            }
        }
        $xml .= '</xml>';
        return $xml;
    }

    /**
     * @desc	按字典序生成签名
     * @param 	array $data 签名数据
     * @param 	string $key 前面key
     * @return 	string
     */
    public static function makeSign(array $data, string $key)
    {
        // 1. 按字典序排序参数
        ksort($data);
        $string = self::signUrlParams($data);
        // 2. 在string后加入KEY
        $string = $string . '&key=' . $key;
        // 3. MD5加密
        $string = md5($string);
        // 4. 所有字符转为大写
        return strtoupper($string);
    }

    /**
     * @desc	签名格式化成url参数
     * @param 	array $params 格式化参数
     * @return 	string
     */
    private static function signUrlParams(array $params)
    {
        $buff = '';
        foreach ($params as $k => $v) {
            if ($k != 'sign' && $v != '' && !is_array($v)) {
                $buff .= $k . '=' . $v . '&';
            }
        }

        $buff = trim($buff, '&');
        return $buff;
    }

    /**
     * @desc 获取毫秒级别的时间戳
     */
    public static function millisecond()
    {
        // 获取毫秒的时间戳
        $time   = explode(' ', microtime());
        $time   = $time[1] . ($time[0] * 1000);
        $time2  = explode('.', $time);
        $time   = $time2[0];

        return $time;
    }

    /**
     * @desc	产生随机字符串，不长于32位
     * @param 	int $length
     * @return 	string 产生的随机字符串
     */
    public static function generateNonceStr($length = 32)
    {
        $chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
        $str   = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * @desc    对emoji表情转义
     * @param   $str
     * @return  string
     */
    public static function emojiEncode($str)
    {
        $strEncode = '';

        $length = mb_strlen($str, 'utf-8');

        for ($i = 0; $i < $length; $i++) {
            $tmpStr = mb_substr($str, $i, 1, 'utf-8');
            if (strlen($tmpStr) >= 4) {
                $strEncode .= '[[EMOJI:' . rawurlencode($tmpStr) . ']]';
            } else {
                $strEncode .= $tmpStr;
            }
        }
        return $strEncode;
    }

    /**
     * @desc    对emoji表情转反义
     * @param   $str
     * @return  string|string[]|null
     */
    public static function emojiDecode($str)
    {
        return preg_replace_callback('|\[\[EMOJI:(.*?)\]\]|', function ($matches) {
            return rawurldecode($matches[1]);
        }, $str);
    }

    /**
     * @desc    判断请求机型
     * @return  string
     */
    public static function deviceType()
    {
        $agent = strtolower($_SERVER['HTTP_USER_AGENT']);
        $type  = 'other';
        //分别进行判断
        if (strpos($agent, 'iphone') || strpos($agent, 'ipad')) {
            $type = 'ios';
        }
        if (strpos($agent, 'android')) {
            $type = 'android';
        }
        return $type;
    }

    /**
     * @desc    记录日志
     * @param   string      $filename   文件名
     * @param   string      $log        日志内容
     * @param   float|int   $fileSize   文件日志大小，默认最大2M，超过则重命名
     */
    public static function log(string $filename, string $log, $fileSize = 2 * 1024 * 1024)
    {
        $path = dirname($filename);
        if (!is_dir($path)) {
            mkdir($path, 0755, true);
        }

        if (is_file($filename) && floor($fileSize) <= filesize($filename)) {
            try {
                rename($filename, dirname($filename) . DIRECTORY_SEPARATOR . time() . '-' . basename($filename));
            } catch (\Exception $e) {
                //
            }
        }

        error_log($log, 3, $filename);
    }

    /**
     * 求两点之间的距离
     * @param $thisLongitude 当前经度
     * @param $thisLatitude 当前纬度
     * @param $stayLongitude 待计算经度
     * @param $stayLatitude 待计算纬度
     * @param int $unit 单位，1米，2千米
     * @param int $decimals 保留小数点几位
     * @return string
     */
    public static function calculateDistance($thisLongitude, $thisLatitude, $stayLongitude, $stayLatitude, $unit = 1, $decimals = 0)
    {
        // 原始坐标纬度弧度 deg2rad()函数将角度转换为弧度
        $oriLatRad = deg2rad($thisLatitude);
        // 新坐标纬度弧度
        $newLatRad = deg2rad($stayLatitude);
        // 坐标纬度弧度差
        $diffLatRad = deg2rad($thisLatitude) - deg2rad($stayLatitude);

        // 经度弧度差
        $diffLngRad = deg2rad($thisLongitude) - deg2rad($stayLongitude);

        // sin 求正弦值 参数单位为弧度 sin(2) => 0.90929742682568
        // cos 求余弦值 参数单位为弧度 cos(0.2) => 0.98006657784124
        // sqrt 求平方根 sqrt(2) => 1.4142135623731
        // asin 求反正弦 参数单位为弧度 asin(2) => 1.4142135623731
        // pow 求幂 pow(2, 2) => 4
        $distance = 2 * asin(sqrt(pow(sin($diffLatRad / 2), 2) + cos($oriLatRad) * cos($newLatRad) * pow(sin($diffLngRad / 2), 2))) * 6378.137 * 1000;
        if ($unit == 2) {
            $distance = $distance / 1000;
        }

        return number_format($distance, $decimals);
    }

    /**
     * 导出csv数据
     * @param $title 表名
     * @param $cell 表头
     * @param $data 数据
     * @param null $closure
     */
    public static function exportCsv($title, $cell, $data, $closure = null)
    {
        set_time_limit(0);
        ini_set('memory_limit', '1024M');
        $output = fopen('php://output', 'w') or die("can't open php://output");
        if (ob_get_contents()) {
            ob_clean();
        }
        header("Content-Type: application/force-download");
        header("Content-type: text/csv;charset=utf-8");
        header("Content-Disposition: attachment; filename=" . $title . ".csv");
        echo chr(0xEF) . chr(0xBB) . chr(0xBF);
        fputcsv($output, $cell);
        foreach ($data as $v) {
            if ($closure instanceof \Closure) {
                $v = $closure($v);
            }
            fputcsv($output, array_values($v));
        }
        fclose($output) or die("can't close php://output");
        exit;
    }



    /**
     * 发送请求
     */

    public static function http_request($url, $data = null)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        if (!empty($data)){
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
        $output = curl_exec($curl);
        curl_close($curl);
        return $output;
    }


    /**
     * 获取对象存储sessionToken
     *
     * @return \think\response\Json
     */
    public static function getOssSessionToken()
    {
        try {
            include'/home/libs/TP6_V3/extend/lc/thirdparty/lib/qcloud-sts-sdk.php';

            $sts = new \STS();
            // 配置参数
            $ossData = Config::get('lc.tencent.oss');
            $config = array(
                'url' => 'https://sts.tencentcloudapi.com/',
                'domain' => 'sts.tencentcloudapi.com',
                'proxy' => '',
                'secretId' => 'AKID4yczHI3XliOnysVrxmKbdPv96mCLDpAE', // 固定密钥
                'secretKey' => 'pNimZ5NO9drpWw61f16vTkkV0FDuMSIr', // 固定密钥
                'bucket' => $ossData['bucket'] ?? 'b1-10012999', // 换成你的 bucket
                'region' => $ossData['region'] ?? 'ap-shanghai', // 换成 bucket 所在园区
                'durationSeconds' => 1800, // 密钥有效期
                // 允许操作（上传）的对象前缀，可以根据自己网站的用户登录态判断允许上传的目录，例子： user1/* 或者 * 或者a.jpg
                // 请注意当使用 * 时，可能存在安全风险，详情请参阅：https://cloud.tencent.com/document/product/436/40265
                'allowPrefix' => 'upload/*',
                // 密钥的权限列表。简单上传和分片需要以下的权限，其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
                'allowActions' => array(
                    // 所有 action 请看文档 https://cloud.tencent.com/document/product/436/31923
                    // 简单上传
                    'name/cos:PutObject',
                    'name/cos:PostObject',
                    // 分片上传
                    'name/cos:InitiateMultipartUpload',
                    'name/cos:ListMultipartUploads',
                    'name/cos:ListParts',
                    'name/cos:UploadPart',
                    'name/cos:CompleteMultipartUpload'
                )
            );
            // 获取临时密钥，计算签名
            $tempKeys = $sts->getTempKeys($config);

            return ['data' => $tempKeys, 'msg' => 'SUCCESS'];
        } catch (\Exception $exception) {
            return ['data' => [], 'msg' => $exception->getMessage()];
        }
    }

}
